/*
 * @(#)XfdmNauck.java        1.0 2000/05/09
 *
 * This file is part of Xfuzzy 3.0, a design environment for fuzzy logic
 * based systems.
 *
 * (c) 2000 IMSE-CNM. The authors may be contacted by the email address:
 *                    xfuzzy-team@imse.cnm.es
 *
 * Xfuzzy is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation.
 *
 * Xfuzzy is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 */

package xfuzzy.xfdm.model.algorithm;

import xfuzzy.lang.*;

import java.util.Vector;

/**
 * Algoritmo de Nauck de reglas m�s correctas
 * 
 * @author Francisco Jos� Moreno Velo
 *
 */
public class XfdmNauck extends XfdmActiveRules {

	//----------------------------------------------------------------------------//
	//                            MIEMBROS PRIVADOS                               //
	//----------------------------------------------------------------------------//

	/**
	 * N�mero de reglas a seleccionar
	 */
	private int numclass;
	
	/**
	 * Indica si el numero de reglas es total o por clase
	 */
	private boolean global;

	//----------------------------------------------------------------------------//
	//                                CONSTRUCTOR                                 //
	//----------------------------------------------------------------------------//

	/**
	 * Constructor completo
	 */
	public XfdmNauck(int numclass, boolean global) {
		this.numclass = numclass;
		this.global = global;
	}

	/**
	 * Constructor con el n�mero de clases
	 */
	public XfdmNauck(int numclass) {
		this.numclass = numclass;
		this.global = true;
	}

	/**
	 * Constructor por defecto
	 */
	public XfdmNauck() {
		this.numclass = 5;
		this.global = true;
	}

	/**
	 * Constructor desde el fichero de configuraci�n
	 */
	public XfdmNauck(double param[]) {
		this.numclass = (int) param[0];
		this.global = (((int) param[1]) == 1);
	}

	//----------------------------------------------------------------------------//
	//                             M�TODOS P�BLICOS                               //
	//----------------------------------------------------------------------------//

	/**
	 * Obtiene un duplicado del objeto
	 */
	public Object clone() {
		return new XfdmNauck(numclass,global);
	}

	/**
	 * Obtiene el nombre del algoritmo
	 */
	public String toString() {
		return "Nauck (Best-performance rule extraction)";
	}

	/**
	 * Representaci�n en el fichero de configuraci�n
	 */
	public String toXML() {
		String eol = System.getProperty("line.separator", "\n");
		String gb = (global? "1": "0");

		String code = "\t\t<algorithm name=\"Nauck\" >"+eol;
		code += "\t\t\t<param name=\"number_of_rules\" value=\""+numclass+"\" ></param>"+eol;
		code += "\t\t\t<param name=\"global\" value=\""+gb+"\" ></param>"+eol;
		code +=	"\t\t</algorithm>";
		return code;
	}

	/**
	 * Indica si el algoritmo es v�lido para problemas de clasificaci�n
	 * @return
	 */
	public boolean supportsClassification() {
		return true;
	}
	
	/**
	 * Indica si el algoritmo es v�lido para problemas de regresi�n
	 * @return
	 */
	public boolean supportsRegression() {
		return false;
	}

	/**
	 * Obtiene el n�mero de reglas a seleccionar
	 */
	public int getNumberOfRules() {
		return numclass;
	}

	/**
	 * Verifica si es el n�mero de reglas total o por clase
	 */
	public boolean isGlobal() {
		return this.global;
	}

	/**
	 * Asigna el n�mero de reglas a seleccionar
	 */
	public void setNumberOfRules(int number) {
		this.numclass = number;
	}

	/**
	 * Asigna si el n�mero de reglas es total o por clase
	 */
	public void setGlobal(boolean global) {
		this.global = global;
	}

	/**
	 * Selecciona las reglas m�s correctas
	 */
	public Vector<XfdmPseudoRule> pruneRules(Vector<XfdmPseudoRule> rules, KnowledgeBase base) {
		if(global) return pruneRulesGlobal(rules,base);
		else return pruneRulesLocal(rules,base);
	}

	//----------------------------------------------------------------------------//
	//                             M�TODOS PRIVADOS                               //
	//----------------------------------------------------------------------------//

	/**
	 * Selecciona las N mejores reglas
	 */
	private Vector<XfdmPseudoRule> pruneRulesGlobal(Vector<XfdmPseudoRule> rules, KnowledgeBase base) {
		int sort[][] = getSortedList(rules,base);
		Vector<XfdmPseudoRule> pruned = new Vector<XfdmPseudoRule>();
		for(int i=0;i<numclass;i++) pruned.add(rules.elementAt(sort[i][0]));
		return pruned;
	}

	/**
	 * Selecciona las N mejores reglas para cada clase
	 */
	private Vector<XfdmPseudoRule> pruneRulesLocal(Vector<XfdmPseudoRule> rules, KnowledgeBase base) {
		Variable[] ovar = base.getOutputs();
		int included[][] = new int[ovar.length][];
		for(int i=0; i<ovar.length; i++) {
			LinguisticLabel lb[] = ovar[i].getType().getAllMembershipFunctions();
			included[i] = new int[lb.length];
		}

		int sorted[][] = getSortedList(rules,base);

		Vector<XfdmPseudoRule> pruned = new Vector<XfdmPseudoRule>();
		for(int i=0; i<sorted.length; i++) {
			if(isFull(included)) break;
			if(!isFull(included,sorted[i])) {
				pruned.add(rules.elementAt(sorted[i][0]));
				increase(included,sorted[i]);
			}
		}
		return pruned;
	}

	/**
	 * Obtiene la lista de reglas ordenada por eficiencia
	 */
	private int[][] getSortedList(Vector<XfdmPseudoRule> rules, KnowledgeBase base) {
		double perform[][] = new double[rules.size()][2];
		for(int i=0; i<perform.length; i++) {
			perform[i][0] = ((XfdmPseudoRule) rules.elementAt(i)).getPerformance();
			perform[i][1] = 1.0*i;
		}

		double aux0, aux1;
		for(int i=0; i<perform.length-1; i++) { 
			for(int j=i+1; j<perform.length; j++) {
				if(perform[i][0]<perform[j][0]) { 
					aux0 = perform[i][0]; aux1 = perform[i][1];
					perform[i][0] = perform[j][0]; perform[i][1] = perform[j][1];
					perform[j][0] = aux0; perform[j][1] = aux1;
				}
			}
		}

		int sorted[][] = new int[rules.size()][base.getOutputs().length+1];
		for(int i=0; i<sorted.length; i++) {
			sorted[i][0] = (int) perform[i][1];
			XfdmPseudoRule pseudo = (XfdmPseudoRule) rules.elementAt(sorted[i][0]);
			int clind[] = pseudo.getClassIndexes();
			for(int j=0; j<clind.length; j++) sorted[i][j+1] = clind[j];
		}
		return sorted;
	}

	/**
	 * Verifica si la cuenta de reglas por clases esta saturada
	 */
	private boolean isFull(int counter[][]) {
		for(int i=0; i<counter.length; i++)
			for(int j=0; j<counter[i].length; j++)
				if(counter[i][j] <numclass) return false;
		return true;
	}

	/**
	 * Verifica si las clases indicadas estan saturadas
	 */
	private boolean isFull(int counter[][], int index[]) {
		for(int i=0; i<counter.length; i++)
			if(counter[i][index[i+1]] <numclass) return false;
		return true;
	}

	/**
	 * Incrementa el contador de las clases indicadas
	 */
	private void increase(int counter[][], int index[]) {
		for(int i=0; i<counter.length; i++) counter[i][index[i+1]]++;
	}
}


